package com.mxpio.erp.inventory.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.mxpio.erp.common.technology.entity.ItemGroup;
import com.mxpio.erp.common.technology.entity.ItemQualityProp;
import com.mxpioframework.common.vo.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mxpio.erp.common.enums.CommonEnums;
import com.mxpio.erp.common.inventory.entity.SupplyChain;
import com.mxpio.erp.common.inventory.service.SupplyChainService;
import com.mxpio.erp.common.jpa.model.BizOrder;
import com.mxpio.erp.common.order.policy.OrderContext;
import com.mxpio.erp.common.purchase.entity.Supply;
import com.mxpio.erp.common.technology.entity.Item;
import com.mxpio.erp.common.vo.OrderExcuteLineVo;
import com.mxpio.erp.common.vo.SupplyChainVo;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.lin.Linu;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.system.service.impl.BaseServiceImpl;

@Service
public class SupplyChainServiceImpl extends BaseServiceImpl<SupplyChain> implements SupplyChainService {
	
	@Value("${mxpio.inv.supplycontrol:true}")
	private boolean supplycontrol;

	@Override
	@Transactional(readOnly = true)
	public Page<Supply> pageBySupply(Pageable pageAble, Criteria c) {
		Page<Supply> page = JpaUtil.linq(Supply.class).where(c).paging(pageAble);
		Set<String> pnCodes = JpaUtil.collect(page.getContent(), "pnCode");
		List<SupplyChain> chains = JpaUtil.linq(SupplyChain.class).collect("itemCode", Item.class,"itemCode").in("pnCode", pnCodes).list();
		Map<String,List<SupplyChain>> chainMap = new HashMap<>();
		for(SupplyChain chain : chains){
			if(chainMap.get(chain.getPnCode()) == null){
				chainMap.put(chain.getPnCode(), new ArrayList<SupplyChain>());
			}
			chainMap.get(chain.getPnCode()).add(chain);
		}
		for(Supply supply : page.getContent()){
			supply.setSupplyChains(chainMap.get(supply.getPnCode()));
		}
		return page;
	}

	@Override
	@Transactional(readOnly = true)
	public Page<Item> pageByItem(Pageable pageAble, Criteria c) {
		Page<Item> page = JpaUtil.linq(Item.class).where(c).paging(pageAble);
		Set<String> itemCodes = JpaUtil.collect(page.getContent(), "itemCode");
		List<SupplyChain> chains = JpaUtil.linq(SupplyChain.class).collect("pnCode", Supply.class,"pnCode").in("itemCode", itemCodes).list();
		Map<String,List<SupplyChain>> chainMap = new HashMap<>();
		for(SupplyChain chain : chains){
			if(chainMap.get(chain.getItemCode()) == null){
				chainMap.put(chain.getItemCode(), new ArrayList<SupplyChain>());
			}
			chainMap.get(chain.getItemCode()).add(chain);
		}
		for(Item item : page.getContent()){
			item.setSupplyChains(chainMap.get(item.getItemCode()));
		}
		
		return page;
	}

	@Override
	@Transactional(readOnly = false)
	public int removeBySupply(String pnCode, Object[] itemCode) {
		return JpaUtil.lind(SupplyChain.class).equal("pnCode", pnCode).in("itemCode", itemCode).delete();
	}

	@Override
	@Transactional(readOnly = false)
	public int removeByItem(String itemCode, Object[] pnCode) {
		return JpaUtil.lind(SupplyChain.class).equal("itemCode", itemCode).in("pnCode", pnCode).delete();
	}

	@Override
	@Transactional(readOnly = false)
	public int editBatchBySupply(String pnCode, Object[] itemCode, SupplyChainVo supplyChainVo) {
		Linu lin = JpaUtil.linu(SupplyChain.class).equal("pnCode", pnCode).in("itemCode", itemCode);
		if(supplyChainVo.getHold() != null){
			lin.set("hold", supplyChainVo.getHold());
		}
		if(supplyChainVo.getInQualityControl() != null){
			lin.set("inQualityControl", supplyChainVo.getInQualityControl());
		}
		if(supplyChainVo.getItemPrice() != null){
			lin.set("itemPrice", supplyChainVo.getItemPrice());
		}
		return lin.update();
	}

	@Override
	@Transactional(readOnly = false)
	public int editBatchByItem(String itemCode, Object[] pnCode, SupplyChainVo supplyChainVo) {
		Linu lin = JpaUtil.linu(SupplyChain.class).equal("itemCode", itemCode).in("pnCode", pnCode);
		if(supplyChainVo.getHold() != null){
			lin.set("hold", supplyChainVo.getHold());
		}
		if(supplyChainVo.getInQualityControl() != null){
			lin.set("inQualityControl", supplyChainVo.getInQualityControl());
		}
		if(supplyChainVo.getDefaultSupply() != null){
			lin.set("defaultSupply", supplyChainVo.getDefaultSupply());
		}
		if(supplyChainVo.getItemPrice() != null){
			lin.set("itemPrice", supplyChainVo.getItemPrice());
		}
		return lin.update();
	}

	@Override
	@Transactional(readOnly = true)
	public void check(OrderContext context) {
		if(supplycontrol) {
			BizOrder order = (BizOrder) context.getEntity();
			List<SupplyChain> chains = JpaUtil.linq(SupplyChain.class).equal("pnCode", order.getPnCode()).list();
			Map<String,SupplyChain> chainMap = JpaUtil.index(chains, "itemCode");
			for(OrderExcuteLineVo vo : context.getOrderExcuteVos()){
				SupplyChain chain = chainMap.get(vo.getItemCode());
				if(chain == null){
					context.addError("物料:" + vo.getItemCode() + "未绑定供应链信息！");
					break;
				}
				if(CommonEnums.YesNo.YES.getCode().equals(chain.getHold())){
					context.addError("物料:" + vo.getItemCode() + "供应链信息冻结！");
					break;
				}
				//物料组、物料、供应链，但凡有一个开启检验，就需要检验
				//先查找物料组
				Item item = JpaUtil.linq(Item.class).equal("itemCode", vo.getItemCode()).findOne();
				ItemGroup groupCode = JpaUtil.linq(ItemGroup.class).equal("groupCode", item.getItemGroupCode()).findOne();
				if (groupCode!=null){
					ItemQualityProp itemGroupProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "group").equal("code", groupCode.getGroupCode()).findOne();
					if(itemGroupProp != null){
						if (CommonEnums.YesNo.YES.getCode().equals(itemGroupProp.getInQualityControl())){
							vo.setInQualityControl(itemGroupProp.getInQualityControl());
							continue;
						}
					}else{
						if (StringUtils.isNotEmpty(groupCode.getFaGroupCode())){
							ItemQualityProp parentGroupProp = this.getParentGroupProp(groupCode.getFaGroupCode());
							if(parentGroupProp != null && CommonEnums.YesNo.YES.getCode().equals(parentGroupProp.getInQualityControl())){
								vo.setInQualityControl(parentGroupProp.getInQualityControl());
								continue;
							}
						}
					}
				}
				//再查找物料
				ItemQualityProp itemProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "item").equal("code", vo.getItemCode()).findOne();
				if(itemProp != null && CommonEnums.YesNo.YES.getCode().equals(itemProp.getInQualityControl())){
					vo.setInQualityControl(itemProp.getInQualityControl());
					continue;
				}
				//最后供应链
				vo.setInQualityControl(chain.getInQualityControl());
			}
		}
	}

	@Override
	@Transactional(readOnly = true)
	public Page<Item> withoutItemBySupply(String pnCode, Pageable pageAble, Criteria c) {
		return JpaUtil
				.linq(Item.class)
				.notExists(SupplyChain.class)
					.equalProperty("itemCode", "itemCode")
					.equal("pnCode", pnCode)
				.end()
				.where(c)
				.paging(pageAble);
	}

	@Override
	@Transactional(readOnly = true)
	public Page<Supply> withoutSupplyByItem(String itemCode, Pageable pageAble, Criteria c) {
		return JpaUtil
				.linq(Supply.class)
				.notExists(SupplyChain.class)
					.equalProperty("pnCode", "pnCode")
					.equal("itemCode", itemCode)
				.end()
				.where(c)
				.paging(pageAble);
	}

	@Override
	@Transactional(readOnly = true)
	public List<Supply> getSupplysByItemCodes(String[] itemCode) {
		List<SupplyChain> supplyChains = JpaUtil.linq(SupplyChain.class).in("itemCode", itemCode).list();
		if (supplyChains.size()>0){
			Map<String, List<SupplyChain>> itemMap = supplyChains.stream().collect(Collectors.groupingBy(SupplyChain::getItemCode));
			Set<String> itemStrs = itemMap.keySet();
			if (itemCode.length>itemStrs.size()){//如果入参长度小于返回的map keys，说明有物料没有供应商信息，此时已经没有必要对比了
				return new ArrayList<>();
			}
			List<String> retainAll = new ArrayList<>();
			int i = 0;
			for (String itemCodeStr:itemStrs){
				i++;
				List<String> pnCodes = itemMap.get(itemCodeStr).stream().map(SupplyChain::getPnCode).collect(Collectors.toList());
				if (i==1){//第一次循环
					retainAll = pnCodes;
					continue;
				}
				retainAll.retainAll(pnCodes);//取交集
			}
			if (retainAll.size()>0){
				return JpaUtil.linq(Supply.class).in("pnCode", retainAll).list();
			}
		}
		return new ArrayList<>();
	}

	@Override
	@Transactional(readOnly = true)
	public List<Supply> getMachinedSupplysByItemCodes(String[] itemCode) {
		List<SupplyChain> supplyChains = JpaUtil.linq(SupplyChain.class).in("itemCode", itemCode).list();
		if (supplyChains.size()>0){
			Map<String, List<SupplyChain>> itemMap = supplyChains.stream().collect(Collectors.groupingBy(SupplyChain::getItemCode));
			Set<String> itemStrs = itemMap.keySet();
			if (itemCode.length>itemStrs.size()){//如果入参长度小于返回的map keys，说明有物料没有供应商信息，此时已经没有必要对比了
				return new ArrayList<>();
			}
			List<String> retainAll = new ArrayList<>();
			int i = 0;
			for (String itemCodeStr:itemStrs){
				i++;
				List<String> pnCodes = itemMap.get(itemCodeStr).stream().map(SupplyChain::getPnCode).collect(Collectors.toList());
				if (i==1){//第一次循环
					retainAll = pnCodes;
					continue;
				}
				retainAll.retainAll(pnCodes);//取交集
			}
			if (retainAll.size()>0){
				return JpaUtil.linq(Supply.class).in("pnCode", retainAll).equal("machinedFlag",CommonEnums.YesNo.YES.getCode()).list();
			}
		}
		return new ArrayList<>();
	}

	@Override
	@Transactional
	public Page<Item> pnCodeOwnedItemsPage(Pageable pageAble, Criteria criteria, String pnCode) {
		List<SupplyChain> chains = JpaUtil.linq(SupplyChain.class).equal("pnCode", pnCode).list();
		Map<String, List<SupplyChain>> scMap = chains.stream().collect(Collectors.groupingBy(SupplyChain::getItemCode));
		if (chains.size()>0){
			Set<String> itemCodeSet= JpaUtil.collect(chains, "itemCode");
			Page<Item> itemPage = JpaUtil.linq(Item.class).in("itemCode", itemCodeSet).where(criteria).paging(pageAble);
			if (itemPage.getContent()!=null&&!itemPage.getContent().isEmpty()){
				for (Item item:itemPage.getContent()){
					List<SupplyChain> supplyChains = scMap.get(item.getItemCode());
					if (supplyChains.size()>0){
						SupplyChain supplyChain = supplyChains.get(0);
						if (supplyChain.getItemPrice()!=null&&supplyChain.getItemPrice().compareTo(BigDecimal.ZERO)!=0){
							item.setItemPrice(supplyChain.getItemPrice());//供应链维护的价格赋值给物料价格
						}
					}
				}
			}
			return itemPage;
		}
		return new PageImpl<>(new ArrayList<>(), pageAble, 0);
	}

	@Override
	@Transactional
	public Result<Item> getItemPrice(String itemCode, String pnCode) {
		SupplyChain supplyChain = JpaUtil.linq(SupplyChain.class)
				.collect("itemCode", Item.class, "itemCode")
				.equal("itemCode", itemCode)
				.equal("pnCode", pnCode).findOne();
		Item item = null;
		if (supplyChain!=null && supplyChain.getItem()!=null){
			item = supplyChain.getItem();
			if (supplyChain.getItemPrice()!=null
					&&supplyChain.getItemPrice().compareTo(BigDecimal.ZERO)!=0){
				item.setItemPrice(supplyChain.getItemPrice());//取供应链中的价格
			}
		}
		if (item==null){
			return Result.error("物料编码未能在供应链中维护或者物料编码在系统中不存在！");
		}
		return Result.OK(item);
	}
	private ItemQualityProp getParentGroupProp(String parentCode){
		if (StringUtils.isBlank(parentCode)){
			return null;
		}
		ItemGroup groupCode = JpaUtil.linq(ItemGroup.class).equal("groupCode", parentCode).findOne();
		if (groupCode==null){
			return null;
		}
		ItemQualityProp itemGroupProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "group").equal("code", groupCode.getGroupCode()).findOne();
		if (itemGroupProp!=null){
			return itemGroupProp;
		}else{
			return getParentGroupProp(groupCode.getFaGroupCode());
		}
	}
}
